<!DOCTYPE html>
<html lang="zh">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>热门电影信息可视化面板</title>
  <script src="https://cdn.jsdelivr.net/npm/echarts@5.5.0/dist/echarts.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/echarts-wordcloud@2.1.0/dist/echarts-wordcloud.min.js"></script>
  <style>
    :root {
      color-scheme: light;
      font-family: "Segoe UI", sans-serif;
    }

    body {
      margin: 0;
      padding: 0;
      background: #f5f6fa;
      color: #2c3e50;
    }

    header {
      padding: 1.5rem 2rem;
      background: linear-gradient(120deg, #f8a5c2, #63cdda);
      color: #1e272e;
    }

    header h1 {
      margin: 0;
      font-size: 1.8rem;
      letter-spacing: 0.05em;
    }

    header p {
      margin: 0.3rem 0 0;
      opacity: 0.8;
      font-size: 0.95rem;
    }

    main {
      padding: 2rem;
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
      gap: 1.5rem;
    }

    .panel {
      background: #ffffff;
      border-radius: 16px;
      box-shadow: 0 10px 24px rgba(31, 45, 61, 0.08);
      padding: 1.5rem;
      display: flex;
      flex-direction: column;
      gap: 0.5rem;
      border: 1px solid rgba(52, 73, 94, 0.08);
    }

    .panel h2 {
      margin: 0;
      font-size: 1.3rem;
      color: #1e2022;
    }

    .chart {
      width: 100%;
      height: 320px;
    }

    footer {
      padding: 1rem 2rem 2rem;
      font-size: 0.9rem;
      color: #576574;
    }

    a {
      color: #0984e3;
    }

    .status {
      font-size: 0.95rem;
      color: #485460;
    }
  </style>
</head>

<body>
  <header>
    <h1>热门电影信息爬取与评分分析系统</h1>
    <p id="dataset-info">载入数据中...</p>
  </header>
  <main>
    <section class="panel">
      <h2>评分最高的前10部电影</h2>
      <div id="top10-chart" class="chart" aria-label="Top 10 电影评分柱状图"></div>
      <p class="status" id="top10-note"></p>
    </section>
    <section class="panel">
      <h2>评分区间分布</h2>
      <div id="rating-pie" class="chart" aria-label="评分区间饼图"></div>
    </section>
    <section class="panel">
      <h2>热门短评词云（实验性）</h2>
      <div id="wordcloud" class="chart" aria-label="短评关键词词云"></div>
      <p class="status">基于短评字段生成，若缺少短评数据则展示占位符。</p>
    </section>
  </main>
  <footer>
    <p>
      请在项目根目录启用本地服务器（如 <code>python -m http.server</code>）后访问，
      以便前端能够读取 <code>output</code> 目录中的数据文件。
    </p>
  </footer>

  <script>
    const MOVIES_URL = "../output/movies.json";
    const TOP10_URL = "../output/top10.json";

    async function fetchJson(url) {
      const response = await fetch(url);
      if (!response.ok) {
        throw new Error(`无法获取 ${url}: ${response.status}`);
      }
      return response.json();
    }

    function bucketizeRating(rating) {
      if (rating >= 9) return "9.0及以上";
      if (rating >= 8) return "8.0-8.9";
      if (rating >= 7) return "7.0-7.9";
      return "小于7.0";
    }

    function renderTop10Chart(dataset) {
      const chart = echarts.init(document.getElementById("top10-chart"));
      const titles = dataset.map(item => item.title);
      const ratings = dataset.map(item => item.rating ? Number(item.rating.toFixed(2)) : null);

      chart.setOption({
        grid: { left: 120, right: 30, top: 40, bottom: 30 },
        xAxis: {
          type: "value",
          name: "评分",
          min: Math.max(Math.min(...ratings) - 0.5, 0)
        },
        yAxis: { type: "category", data: titles, inverse: true },
        series: [{
          type: "bar",
          data: ratings,
          label: {
            show: true,
            position: "right",
            formatter: params => params.value ? params.value.toFixed(1) : "NA"
          },
          itemStyle: {
            color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
              { offset: 0, color: "#f6d365" },
              { offset: 1, color: "#fda085" }
            ])
          }
        }],
        tooltip: {
          trigger: "axis",
          axisPointer: { type: "shadow" },
          formatter: params => {
            const item = dataset[params[0].dataIndex];
            const voteText = item.votes ? `${item.votes}人评价` : "评价人数未知";
            return `
              <strong>${item.title}</strong><br/>
              评分：${item.rating ?? "未知"}<br/>
              ${voteText}<br/>
              ${item.quote ?? ""}
            `;
          }
        }
      });
      document.getElementById("top10-note").textContent =
        "数据更新于 " + new Date().toLocaleString();
      window.addEventListener("resize", () => chart.resize());
    }

    function renderRatingPie(dataset) {
      const buckets = {
        "9.0及以上": 0,
        "8.0-8.9": 0,
        "7.0-7.9": 0,
        "小于7.0": 0
      };

      dataset
        .map(item => item.rating)
        .filter(rating => typeof rating === "number")
        .forEach(rating => {
          const key = bucketizeRating(rating);
          buckets[key] += 1;
        });

      const seriesData = Object.entries(buckets)
        .filter(([, value]) => value > 0)
        .map(([name, value]) => ({ name, value }));

      const chart = echarts.init(document.getElementById("rating-pie"));

      if (seriesData.length === 0) {
        chart.setOption({
          title: { text: "暂无评分区间数据", left: "center", top: "center" }
        });
        return;
      }

      chart.setOption({
        tooltip: {
          trigger: "item",
          formatter: p => `${p.name}: ${p.value} 部 (${p.percent}%)`
        },
        series: [{
          name: "评分区间",
          type: "pie",
          radius: ["45%", "75%"],
          data: seriesData,
          label: { formatter: "{b}: {d}%" }
        }]
      });

      window.addEventListener("resize", () => chart.resize());
    }

    function renderWordCloud(dataset) {
      const phrases = dataset
        .map(item => item.quote)
        .filter(Boolean)
        .flatMap(text =>
          text
            .replace(/[“”"、，。！？!?,.]/g, " ")
            .split(/\s+/)
            .filter(token => token.length >= 2)
        );
      const freq = phrases.reduce((acc, word) => {
        acc[word] = (acc[word] || 0) + 1;
        return acc;
      }, {});

      const data = Object.entries(freq).map(([name, value]) => ({ name, value }));
      const chart = echarts.init(document.getElementById("wordcloud"));
      if (data.length === 0) {
        chart.setOption({
          title: { text: "暂无短评数据", left: "center", top: "center", textStyle: { fontSize: 18 } }
        });
        return;
      }
      chart.setOption({
        tooltip: { show: true },
        series: [{
          type: "wordCloud",
          gridSize: 12,
          sizeRange: [12, 42],
          rotationRange: [-45, 90],
          shape: "circle",
          width: "100%",
          height: "100%",
          drawOutOfBound: false,
          textStyle: {
            fontFamily: "sans-serif",
            color: () => {
              const colors = ["#74b9ff", "#a29bfe", "#fd79a8", "#ffeaa7", "#55efc4"];
              return colors[Math.floor(Math.random() * colors.length)];
            }
          },
          data
        }]
      });
      window.addEventListener("resize", () => chart.resize());
    }

    async function init() {
      try {
        const [movies, top10] = await Promise.all([
          fetchJson(MOVIES_URL),
          fetchJson(TOP10_URL)
        ]);
        document.getElementById("dataset-info").textContent =
          `已加载 ${movies.length} 部影片数据。`;
        renderTop10Chart(top10.length ? top10 : movies.slice(0, 10));
        renderRatingPie(movies);
        renderWordCloud(movies);
      } catch (error) {
        console.error(error);
        document.getElementById("dataset-info").textContent =
          "数据加载失败：请确认已运行爬虫并生成 output 目录。";
      }
    }

    init();
  </script>
</body>

</html>
